/***************************************************************************
 *   Copyright (C) 2010 by Oleg Goncharov  *
 *   $EMAIL$                           *                          
 *                                                                         *
 *   This file is part of ChessVision.                                     *
 *                                                                         *
 *   ChessVision is free software; you can redistribute it and/or modify   *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include <opencv/cv.h>

#include "cimageobject.h"

CImageObject::CImageObject(IplImage * src, IplImage * seg, const CvConnectedComp& obj) : rect(obj.rect) {
	img = cvCreateImage(cvSize(rect.width, rect.height), IPL_DEPTH_8U, 3);
	mask = cvCreateMat(rect.height, rect.width, CV_8UC1);
	

	cvSetImageROI(seg, rect);	
	cvXorS(seg, obj.value, img);
	cvResetImageROI(seg);
	cvCvtColor(img, mask, CV_BGR2GRAY);
	cvCmpS(mask, 0, mask, CV_CMP_EQ);
	
	cvSetImageROI(src, rect);
	cvCopy(src, img);
	cvResetImageROI(src);
	
	area = obj.area;
	perimeter = 0;
	m_is_calulated = false;
	avg_color = 0;
}

CImageObject::CImageObject(IplImage * src, CvMat * _mask, const CvConnectedComp& obj) : rect(obj.rect) {
	CvMat submat;
	
	img = cvCreateImage(cvSize(rect.width, rect.height), IPL_DEPTH_8U, 3);
	mask = cvCreateMat(rect.height, rect.width, CV_8UC1);
	
	cvSetImageROI(src, rect);
	cvCopy(src, img);
	cvResetImageROI(src);
	cvCopy(cvGetSubRect(_mask, &submat, rect), mask);
	
	area = obj.area;
	perimeter = 0;
	m_is_calulated = false;
	avg_color = 0;
}

CImageObject::CImageObject(const CImageObject& o) {
	img = cvCloneImage(o.img);
	mask = cvCloneMat(o.mask);
	rect = o.rect;
	area = o.area;
	if (o.perimeter) {
		perimeter = new double; 
		perimeter = o.perimeter;
	}
	else perimeter = 0;
	if (o.avg_color) {
		avg_color = new CvScalar;
		avg_color = o.avg_color;
	}
	else avg_color = 0;
	if (m_is_calulated = o.m_is_calulated) m = o.m;
}

void CImageObject::Draw(IplImage * img) {
	cvRectangle(img, cvPoint(rect.x, rect.y), cvPoint(rect.x + rect.width, rect.y + rect.height), cvScalar(0,255,0),1);
	cv::Point2f com = CenterOfMass();
	cvSetImageROI(img, rect);
	cvSet(img, AvgColor(), mask);
	cvResetImageROI(img);
	cvCircle(img, com , 2, cvScalar(0,0,255));
}

void CImageObject::Clear() {
	cvReleaseImage(&img);
	cvReleaseMat(&mask);
	if (perimeter) delete perimeter;
	if (avg_color) delete avg_color;
}

double CImageObject::Perimeter() {
	if (perimeter) return *perimeter;
	perimeter = new double;
	
	//TODO Не самый экономичный способ
	CvMat * tmp = cvCreateMat(mask->rows, mask->cols, CV_8UC1);
	cvDilate(mask, tmp, 0, 1);
	cvSet(tmp, cvRealScalar(0), mask);
	*perimeter = cvCountNonZero(tmp);
	cvReleaseMat(&tmp);
	
	return *perimeter;
}

double CImageObject::Elongation() {
	if (!m_is_calulated) CalcMoments();
	double delta = cvGetCentralMoment(&m, 0, 2) + cvGetCentralMoment(&m, 2, 1);
	double root = sqrt(delta*delta + 4*cvGetCentralMoment(&m, 1, 1)*cvGetCentralMoment(&m, 1, 1));
	return (delta + root) / (delta - root);
}

CvScalar CImageObject::AvgColor() {
	if (avg_color) return *avg_color;
	avg_color = new CvScalar;
	return *avg_color = cvAvg(img, mask);
}

bool CImageObject::Merge(CImageObject& o, int distance, bool force) {
	CvRect nrect;
	// calc new rect
	nrect.x = MIN(rect.x, o.rect.x);
	nrect.y = MIN(rect.y, o.rect.y);
	nrect.width = MAX(rect.x + rect.width, o.rect.x + o.rect.width) - nrect.x;
	nrect.height = MAX(rect.y + rect.height, o.rect.y + o.rect.height) - nrect.y;
	
	CvRect r1 = rect; r1.x -= nrect.x; r1.y -= nrect.y;
	CvRect r2 = o.rect; r2.x -= nrect.x; r2.y -= nrect.y;
	
	// create common mask
	CvMat * nmask = cvCreateMat(nrect.height, nrect.width, CV_8UC1);
	IplImage tmp;
	IplImage * mask_img = cvGetImage(nmask, &tmp);
	
	cvSetZero(mask_img); 
	cvSetImageROI(mask_img, r1);
	cvCopy(mask, mask_img); 
	cvResetImageROI(mask_img);
	if (!force) {
		// check whether one mask touch another
		IplImage * testmask_img = cvCreateImage( cvGetSize(nmask), IPL_DEPTH_8U, 1);
		IplConvKernel * kern =  cvCreateStructuringElementEx(1+2*distance, 1+2*distance, 1+distance, 1+distance, CV_SHAPE_ELLIPSE);
		
		cvDilate(mask_img, testmask_img, kern);
		cvSetImageROI(testmask_img, r2);
		cvAnd(testmask_img, o.mask, testmask_img);
		
		if (cvCountNonZero(testmask_img)) force = true;
		cvReleaseImage(&testmask_img);
		cvReleaseStructuringElement(&kern);
	}
	if (force) { // we should merge objects
		cvSetImageROI(mask_img, r2);
		cvOr(mask_img, o.mask, mask_img);
		cvResetImageROI(mask_img);
		
		IplImage * nimg = cvCreateImage( cvGetSize(nmask), img->depth , img->nChannels);
		cvSetImageROI(nimg, r1);
		cvCopy(img, nimg, mask);
		cvSetImageROI(nimg, r2);
		cvCopy(o.img, nimg, o.mask);
		cvResetImageROI(nimg);
		
		Clear();
		img = nimg;
		mask = nmask;
		rect = nrect;
		m_is_calulated = false;
		CalcMoments();
		area = cvGetSpatialMoment(&m, 0, 0);
		perimeter = 0;
		avg_color = 0;
		
		return true;
	}
	cvReleaseMat(&nmask);
	return false;
}
	
